#include "e2d/core/render/SpriteRenderer.h"
#include "e2d/core/trans/TransformNode.h"

e2d::SpriteRenderer::SpriteRenderer()
{
	m_PickupEffect = std::make_shared<e2d::PickupEffect>();
	m_ImageEffect = std::make_shared<e2d::ImageEffect>();
	m_GBufferEffect = std::make_shared<e2d::GBufferEffect>();
	m_hdrSkyboxDebugEffect = std::make_shared<e2d::SkyboxEffect>();

	// phong
	m_URPRenderer = std::make_shared<e2d::URPRenderer>();

	// Phong Object
	m_FontEffect = std::make_shared<e2d::FontEffect>(e2d::FontEffect::DefaultragCode());
}

void e2d::SpriteRenderer::SetInst(std::shared_ptr<e2d::Scene> scene)
{
	e2d::EntityHandler::SetInst(scene);
	m_URPRenderer->SetInst(scene);
}


void e2d::SpriteRenderer::SetScreenSize(const glm::ivec2& screenSize)
{
	m_ScreenSize = screenSize;
}

void e2d::SpriteRenderer::OnLoad()
{
	LoadView<e2d::CameraViewEdit>();
	LoadView<e2d::CameraView>();
	LoadView<e2d::ImageQuad>();

	LoadView<e2d::FixedMeshRender>();
	LoadView<e2d::SkinnedMeshRender>();
	LoadView<e2d::LightSource>();
	LoadView<e2d::Texts>();
	LoadView<e2d::LightEnv>();
	LoadView<e2d::SkyboxView>();

	m_URPRenderer->OnLoad();
}


void e2d::SpriteRenderer::OnRender()
{
	
	InitLight();

	auto cameraViews = m_Scene->GetComponentEntities<e2d::CameraView>();
	auto& buffers = fxcc::graph::opengl3::RenderStates::Inst().m_Buffers;

	for (auto cameraObj : cameraViews)
	{
		if (!cameraObj->ExistCalculate()) continue;
		auto& cameraView = cameraObj->Get<e2d::CameraView>();
		const auto& cameraTransform = cameraObj->GetOrEmplace<e2d::TransformNode>();

		e2d::Effect3d::PassData passData;
		cameraView.UpdatePassData(passData);
		buffers->SetPassData(passData);
		
		cameraView.BeginRTT();
		RenderSkybox();
		RenderPass();
		cameraView.EndRTT();
	}

	auto cameraViewEdits = m_Scene->GetComponentEntities<e2d::CameraViewEdit>();
	for (auto cameraObj : cameraViewEdits)
	{
		if (!cameraObj->ExistCalculate()) continue;

		auto& cameraViewEdit = cameraObj->Get<e2d::CameraViewEdit>();
		const auto& cameraTransform = cameraObj->GetOrEmplace<e2d::TransformNode>();

		e2d::Effect3d::PassData passData;
		cameraViewEdit.UpdatePassData(passData);
		buffers->SetPassData(passData);

		cameraViewEdit.BeginRTT();
		RenderSkybox();
		RenderPass();
		cameraViewEdit.EndRTT();

		cameraViewEdit.BeginGBufferRTT();
		e2d::Effect3d::ClearBgColor({ 0,0,0,1 });
		e2d::Effect3d::ClearBuffer(true, true);
		RenderPassGBuffer();
		cameraViewEdit.EndGBufferRTT();

		// Pickup renderTarget
		cameraViewEdit.BeginPickRTT();
		RenderPickupPass(cameraObj);
		cameraViewEdit.EndPickRTT();
	}
}

void e2d::SpriteRenderer::OnUpdate()
{
	UpdateView<e2d::SkinnedMeshRender>();
	UpdateView<e2d::FixedMeshRender>();

}


void e2d::SpriteRenderer::OnUpdateRuntime()
{
	UpdateViewRuntime<e2d::SkinnedMeshRender>();
	UpdateViewRuntime<e2d::FixedMeshRender>();

}

void e2d::SpriteRenderer::OnRenderRuntime()
{
	auto& buffers = fxcc::graph::opengl3::RenderStates::Inst().m_Buffers;

	m_URPRenderer->BindingPass();

	auto cameraObj = m_Scene->m_MainCamera;

	if (cameraObj && cameraObj.m_Entity->HasComponent<e2d::CameraView>())
	{
		const auto& cameraMain = cameraObj.m_Entity->GetConst<e2d::CameraView>();

		auto texture = cameraMain.m_RenderTarget->GetTexture(0);
		
		m_ImageEffect->Begin();

		e2d::Effect3d::Viewport(0, 0, m_ScreenSize.x, m_ScreenSize.y);
		e2d::Effect3d::ClearBgColor(cameraMain.m_BackgroundColor);
		e2d::Effect3d::ClearBuffer(cameraMain.m_ClearColor, cameraMain.m_ClearDepthStencil);


		{
			fxcc::graph::common::OrthoCamera orthoCamera;
			fxcc::graph::common::Transform quadTransform;

			glm::vec2 m_Size;
			m_Size.x = texture->m_Desc.m_MipmapData.m_Width;
			m_Size.y = texture->m_Desc.m_MipmapData.m_Height;

			orthoCamera.SetPosition(glm::vec3(0.0f, 0.0f, 1.0f));
			orthoCamera.LookAt(glm::vec3(0.0f));
			orthoCamera.SetHalfExtends(glm::vec3(m_Size.x * 2, m_Size.y * 2, 100.0f));

			quadTransform.m_Scale = glm::vec3(m_Size.x * 2.0f, m_Size.y * 2.0f, 1.f);

			e2d::Effect3d::PassData passData;
			e2d::Effect3d::ObjData objData;
			passData.Load2(orthoCamera);
			objData.Load(quadTransform);

			buffers->SetPassData(passData);
			buffers->SetObjData(objData);


		}

		m_ImageEffect->DebugTexture2DJack(texture.get(), 0, "imageMap");
		m_ImageEffect->End();
	}
}
;


void e2d::SpriteRenderer::RenderPass()
{
	m_URPRenderer->OnRender();
}

void e2d::SpriteRenderer::RenderPassGBuffer()
{
	m_GBufferEffect->Begin();

	//for (auto e : m_Scene->GetComponentEntities<e2d::MagicTower>())
	//{
	//	const auto& magicTower = e->GetConst<e2d::MagicTower>();

	//	auto icons = magicTower.RenderEntities();

	//	for (auto icon : icons)
	//	{
	//		const auto& transformNode = icon->GetConst<e2d::TransformNode>();
	//		e2d::Effect3d::ObjData objData(transformNode.m_World);
	//		objData.invertNormal = false;
	//		
	//		m_ObjBuffer->Upload<e2d::Effect3d::ObjData>(objData);
	//		m_GBufferEffect->Begin();
	//		m_GBufferEffect->InstMeshJack(m_GBufferEffect->m_QuadMesh.get());
	//	}
	//}

	m_GBufferEffect->End();

}

void e2d::SpriteRenderer::RenderPassTexts()
{
	auto& buffers = fxcc::graph::opengl3::RenderStates::Inst().m_Buffers;

	m_FontEffect->Begin();

	auto textss = m_Scene->GetComponentEntities<e2d::Texts>();

	for (const auto textsEntity : textss)
	{
		if (textsEntity->ExistCalculate())
		{
			const auto& texts = textsEntity->GetConst<e2d::Texts>();
			auto textsTransform = textsEntity->GetConst<e2d::TransformNode>();

			textsTransform.m_World.m_Scale = glm::vec3(1.0f);
			m_FontEffect->SetObjData(textsTransform.m_World);
			m_FontEffect->RenderTextLine(texts.m_TextLine);
		}

	}
	m_FontEffect->End();
}


void e2d::SpriteRenderer::RenderPickupPass(e2d::Entity* cameraObj)
{
	auto& buffers = fxcc::graph::opengl3::RenderStates::Inst().m_Buffers;
	buffers->Binding();

	auto& cameraViewEdit = cameraObj->Get<e2d::CameraViewEdit>();

	m_PickupEffect->Begin();
	m_PickupEffect->SetFixed();
	auto fixedMeshes = m_Scene->GetComponentEntities<e2d::FixedMeshRender>();

	for (auto entity : fixedMeshes) {
		if (!entity->ExistCalculate())continue;

		auto& transformNode = entity->GetOrEmplace<e2d::TransformNode>();
		const auto& fixedMeshRender = entity->GetConst<e2d::FixedMeshRender>();


		auto mesh = fixedMeshRender.GetMesh();

		if (mesh) {

			e2d::Effect3d::ObjData objData;
			objData.Load(transformNode.m_World);
			objData.objId = ++cameraViewEdit.m_PickupIndex;
			buffers->SetObjData(objData);
			mesh->Bind();
			mesh->DrawElementsAuto();
			cameraViewEdit.RegisterPickup(objData.objId, entity);

		}

	}

	auto skinnedMeshRenders = m_Scene->GetComponentEntities<e2d::SkinnedMeshRender>();

	m_PickupEffect->SetSkinned();
	for (auto entity : skinnedMeshRenders) {
		if (!entity->ExistCalculate())continue;

		auto& transformNode = entity->GetOrEmplace<e2d::TransformNode>();
		const auto& skinnedMeshRender = entity->Get<e2d::SkinnedMeshRender>();
		
		auto mesh = skinnedMeshRender.GetMesh();

		if (mesh) {

			e2d::Effect3d::ObjData objData;
			objData.Load(transformNode.m_World);
			objData.objId = ++cameraViewEdit.m_PickupIndex;

			buffers->SetObjData(objData);
			buffers->SetBoneData(skinnedMeshRender.m_BoneData);

			mesh->Bind();
			mesh->DrawElementsAuto();
			cameraViewEdit.RegisterPickup(objData.objId, entity);

		}
	}
	m_PickupEffect->End();

}

void e2d::SpriteRenderer::InitLight()
{
	auto& buffers = fxcc::graph::opengl3::RenderStates::Inst().m_Buffers;

	buffers->Binding();


	{
		auto lights = m_Scene->GetComponentEntities<e2d::LightSource>();

		fxcc::graph::common::LightEffect lightEffect;
		for (const auto entity : lights)
		{
			const auto& lightSource = entity->GetConst<e2d::LightSource>();
			lightSource.UpdateLightEffect(lightEffect);
		}
		buffers->SetLightEffectData(lightEffect);
	}

	{
		auto lightEnvs = m_Scene->GetComponentEntities<e2d::LightEnv>();

		for (const auto entity : lightEnvs)
		{
			const auto& lightEnv = entity->GetConst<e2d::LightEnv>();
			lightEnv.InitLight();

		}
		fxcc::graph::opengl3::IBuffers::GetUniformPart3d();
	}
}

void e2d::SpriteRenderer::RenderSkybox()
{
	const auto skyboxsView = m_Scene->GetComponentEntities<e2d::SkyboxView>();

	m_hdrSkyboxDebugEffect->Begin();
	for (const auto entity : skyboxsView)
	{
		if (!entity->ExistCalculate()) continue;

		const auto skyboxView = entity->GetConst<e2d::SkyboxView>();
		if (skyboxView.m_SkyboxTexture.m_Value)
		{
			m_hdrSkyboxDebugEffect->RenderSkybox(skyboxView.m_SkyboxTexture.m_Value.get());
		}
	}
	m_hdrSkyboxDebugEffect->End();
}
